/*
 * Copyright (C) 2024 Huawei Device Co., Ltd.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

import { MarsTaskProperty } from './MarsTaskProperty';
import PushMessage from './PushMessage';
import PushMessageHandler from './PushMessageHandler';
import MarsPushMessageFilter from './MarsPushMessageFilter';
import MarsServiceNative from '../service/MarsServiceNative';
import MarsServiceProfile from '../service/MarsServiceProfile';
import { MarsServiceStub } from '../service/MarsServiceStub';
import AbstractTaskWrapper from './AbstractTaskWrapper'
import { AppLogic, GlobalContext } from '@ohos/mars';
import { Context } from '@ohos.abilityAccessCtrl';

class MarsServiceProxy {
  // public static final String SERVICE_ACTION = "BIND_MARS_SERVICE";
  public static readonly SERVICE_DEFAULT_CLASSNAME: string = "sample.wrapper.service.MarsServiceNative";

  private static service: MarsServiceStub = null;

  public static GLOBAL_CMD_ID_MAP: Map<string, number> = new Map();

  public static instance: MarsServiceProxy = null;
  private packageName: string;
  private className: string;
  public accountInfo: AppLogic.AccountInfo;

  private pushMessageHandlerHashMap: Map<number, PushMessageHandler> = new Map();
  private filter: MarsPushMessageFilter = null;

  public static getInstance(): MarsServiceProxy {
    console.info("getInstance MarsServiceProxy.instance:" + MarsServiceProxy.instance);
    if (!MarsServiceProxy.instance) {
        MarsServiceProxy.instance=new MarsServiceProxy()
        let profile: MarsServiceProfile = MarsServiceNative.gFactory.createMarsServiceProfile();
        this.service = new MarsServiceStub(profile);
        console.info("getInstance MarsServiceProxy.instance:" + this.service);
    }
    console.info("getInstance return MarsServiceProxy.instance:" + MarsServiceProxy.instance);
    return MarsServiceProxy.instance;
  }

  public static getService(): MarsServiceStub {
    return this.service;
  }

  public init(packageName: string): void {
    let context = GlobalContext.getContext().getValue('context') as Context;
    this.packageName = (packageName=='' ? context.applicationInfo.name : packageName);
    this.className = MarsServiceProxy.SERVICE_DEFAULT_CLASSNAME;
    this.startService();
  }

  public static setOnPushMessageListener(cmdId: number, pushMessageHandler: PushMessageHandler): void {
    if (pushMessageHandler == null) {
      MarsServiceProxy.getInstance().pushMessageHandlerHashMap.delete(cmdId);
    } else {
      MarsServiceProxy.getInstance().pushMessageHandlerHashMap.set(cmdId, pushMessageHandler);
    }
  }

  public static send(taskWrapper: AbstractTaskWrapper): void {
    if (taskWrapper == null) {
      // Stop, no more task

      return;
    }

    try {
      console.info("sending task = " + taskWrapper);
      const cgiPath: string = taskWrapper.getProperties().get(MarsTaskProperty.OPTIONS_CGI_PATH);
      const globalCmdID: number = MarsServiceProxy.GLOBAL_CMD_ID_MAP.get(cgiPath);
      if (globalCmdID != null) {
        taskWrapper.getProperties().set(MarsTaskProperty.OPTIONS_CMD_ID, globalCmdID);
        console.info("overwrite cmdID with global cmdID Map: " + cgiPath + " -> " + globalCmdID);
      }
      const taskID: number = this.getService().send(taskWrapper);
      // NOTE: Save taskID to taskWrapper here
      taskWrapper.getProperties().set(MarsTaskProperty.OPTIONS_TASK_ID, taskID);

    } catch (e) {
      console.info("send exception:" + e);
    }
  }

  public static cancel(marsTaskWrapper: AbstractTaskWrapper): void {
    MarsServiceProxy.getInstance().cancelSpecifiedTaskWrapper(marsTaskWrapper);
  }

  public setForeground(isForeground: number): void {
    try {
      if (MarsServiceProxy.getService() == null) {
        this.startService();
        return;
      }
      MarsServiceProxy.getService().setForeground(isForeground ? 1 : 0);
    } catch (e) {
      console.info("setForeground exception:" + e);
    }
  }

  private startService() {
    console.info("");
    let _this = this;

    this.filter = new class MarsPushMessageFilter {
      public onRecv(cmdId: number, buffer: ArrayBuffer): boolean {
        let handler: PushMessageHandler = _this.pushMessageHandlerHashMap.get(cmdId);
        if (handler != null) {
          console.info("processing push message, cmdid = " + cmdId + ", buffer:" + new Uint8Array(buffer));
          let message = new PushMessage(cmdId, buffer);
          handler.process(message);
          return true;
        } else {
          console.info("no push message listener set for cmdid = " + cmdId + ", just ignored");
        }
        return false;
      }
    };
    console.info("startService 001");
    MarsServiceProxy.getService().registerPushMessageFilter(this.filter);
  }

  private stopService() {
    MarsServiceProxy.getService().unregisterPushMessageFilter(this.filter);
  }

  private cancelSpecifiedTaskWrapper(marsTaskWrapper: AbstractTaskWrapper): void {
    // Already sent to remote service, need to cancel it
    try {
      MarsServiceProxy.getService().cancel(marsTaskWrapper.getProperties().get(MarsTaskProperty.OPTIONS_TASK_ID));
    } catch (e) {
      console.info("cancel mars task wrapper in remote service failed, I'll make marsTaskWrapper.onTaskEnd");
    }
  }
}

export default MarsServiceProxy;